import { useFrame, useThree } from '@react-three/fiber';
import React, { useRef } from 'react';
import * as THREE from 'three';

import sceneStore from '@/stores/scene';

const geometry = new THREE.BoxGeometry(80, 50, 50, 1, 1, 1);
const positions1 = geometry.getAttribute('position');
positions1.setY(4, positions1.getY(4) - 10);
positions1.setZ(4, positions1.getZ(4) + 20);
positions1.setY(8, positions1.getY(8) - 10);
positions1.setZ(8, positions1.getZ(8) + 20);
positions1.setY(21, positions1.getY(21) - 10);
positions1.setZ(21, positions1.getZ(21) + 20);

positions1.setY(5, positions1.getY(5) - 10);
positions1.setZ(5, positions1.getZ(5) - 20);
positions1.setY(10, positions1.getY(10) - 10);
positions1.setZ(10, positions1.getZ(10) - 20);
positions1.setY(16, positions1.getY(16) - 10);
positions1.setZ(16, positions1.getZ(16) - 20);

positions1.setY(6, positions1.getY(6) + 30);
positions1.setZ(6, positions1.getZ(6) + 20);
positions1.setY(14, positions1.getY(14) + 30);
positions1.setZ(14, positions1.getZ(14) + 20);
positions1.setY(23, positions1.getY(23) + 30);
positions1.setZ(23, positions1.getZ(23) + 20);

positions1.setY(7, positions1.getY(7) + 30);
positions1.setZ(7, positions1.getZ(7) - 20);
positions1.setY(12, positions1.getY(12) + 30);
positions1.setZ(12, positions1.getZ(12) - 20);
positions1.setY(18, positions1.getY(18) + 30);
positions1.setZ(18, positions1.getZ(18) - 20);

const geomPropeller = new THREE.BoxGeometry(20, 10, 10, 1, 1, 1);
const positions2 = geomPropeller.getAttribute('position');
positions2.setY(4, positions2.getY(4) - 5);
positions2.setZ(4, positions2.getZ(4) + 5);
positions2.setY(8, positions2.getY(8) - 5);
positions2.setZ(8, positions2.getZ(8) + 5);
positions2.setY(21, positions2.getY(21) - 5);
positions2.setZ(21, positions2.getZ(21) + 5);

positions2.setY(5, positions2.getY(5) - 5);
positions2.setZ(5, positions2.getZ(5) - 5);
positions2.setY(10, positions2.getY(10) - 5);
positions2.setZ(10, positions2.getZ(10) - 5);
positions2.setY(16, positions2.getY(16) - 5);
positions2.setZ(16, positions2.getZ(16) - 5);

positions2.setY(6, positions2.getY(6) + 5);
positions2.setZ(6, positions2.getZ(6) + 5);
positions2.setY(14, positions2.getY(14) + 5);
positions2.setZ(14, positions2.getZ(14) + 5);
positions2.setY(23, positions2.getY(23) + 5);
positions2.setZ(23, positions2.getZ(23) + 5);

positions2.setY(7, positions2.getY(7) + 5);
positions2.setZ(7, positions2.getZ(7) - 5);
positions2.setY(12, positions2.getY(12) + 5);
positions2.setZ(12, positions2.getZ(12) - 5);
positions2.setY(18, positions2.getY(18) + 5);
positions2.setZ(18, positions2.getZ(18) - 5);

const suspensionGeom = new THREE.BoxGeometry(4, 20, 4);
suspensionGeom.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 10, 0));

const AirPlane = () => {
  const mode = sceneStore(state => state.mode);

  const airplane = useRef<THREE.Group>(null!);
  const propeller = useRef<THREE.Mesh>(null!);
  const pointer = useThree(state => state.pointer);

  useFrame((state, delta) => {
    const targetY = normalize(pointer.y, -0.75, 0.75, 50, 190);
    const targetX = normalize(pointer.x, -0.75, 0.75, -100, -20);

    // Move the plane at each frame by adding a fraction of the remaining distance
    airplane.current.position.y += (targetY - airplane.current.position.y) * 0.1;
    airplane.current.position.x += (targetX - airplane.current.position.x) * 0.1;

    // Rotate the plane proportionally to the remaining distance
    airplane.current.rotation.z = (targetY - airplane.current.position.y) * 0.0128;
    airplane.current.rotation.x = (airplane.current.position.y - targetY) * 0.0064;
    airplane.current.rotation.y = (airplane.current.position.x - targetX) * 0.0064;

    propeller.current.rotation.x += delta * 500;
  });

  return (
    <group ref={airplane} scale={0.35} position={[-40, 110, -250]}>
      {/* cabin */}
      <mesh castShadow receiveShadow geometry={geometry}>
        <meshPhongMaterial color={0xf25346} flatShading />
      </mesh>
      {/* engine */}
      <mesh position={[40, 0, 0]} castShadow receiveShadow>
        <boxGeometry args={[20, 50, 50, 1, 1, 1]} />
        <meshPhongMaterial color={0xd8d0d1} flatShading />
      </mesh>
      {/* tail */}
      <mesh position={[-35, 25, 0]} castShadow receiveShadow>
        <boxGeometry args={[15, 20, 5, 1, 1, 1]} />
        <meshPhongMaterial color={0xf25346} flatShading />
      </mesh>
      {/* wing */}
      <mesh position={[20, 12, 0]} castShadow receiveShadow>
        <boxGeometry args={[40, 4, 150, 1, 1, 1]} />
        <meshPhongMaterial color={0xf25346} flatShading />
      </mesh>
      <mesh position={[20, -3, 0]} castShadow receiveShadow>
        <boxGeometry args={[40, 4, 150, 1, 1, 1]} />
        <meshPhongMaterial color={0xf25346} flatShading />
      </mesh>
      {/* windshield */}
      <mesh position={[5, 27, 0]} castShadow receiveShadow>
        <boxGeometry args={[3, 15, 20, 1, 1, 1]} />
        <meshPhongMaterial
          color={0xd8d0d1}
          flatShading
          transparent
          opacity={0.3}
          emissive={mode === 'night' ? 0x0f0 : 0x000}
        />
      </mesh>
      {/* propeller */}
      <mesh ref={propeller} position={[50, 0, 0]} castShadow receiveShadow geometry={geomPropeller}>
        <meshPhongMaterial color={0x59332e} flatShading />
        <mesh position={[8, 0, 0]} castShadow receiveShadow>
          <boxGeometry args={[1, 100, 10, 1, 1, 1]} />
          <meshPhongMaterial
            color={0x23190f}
            flatShading
            emissive={mode === 'night' ? 0xff190f : 0x000}
          />
        </mesh>
        <mesh position={[8, 0, 0]} castShadow receiveShadow>
          <boxGeometry args={[1, 10, 100, 1, 1, 1]} />
          <meshPhongMaterial
            color={0x23190f}
            flatShading
            emissive={mode === 'night' ? 0xff190f : 0x000}
          />
        </mesh>
      </mesh>
      {/* wheelProtecR */}
      <mesh position={[25, -20, 25]} castShadow receiveShadow>
        <boxGeometry args={[30, 15, 10, 1, 1, 1]} />
        <meshPhongMaterial color={0xd8d0d1} flatShading />
      </mesh>
      {/* wheelTireR */}
      <mesh position={[25, -28, 25]} castShadow receiveShadow>
        <boxGeometry args={[24, 24, 4]} />
        <meshPhongMaterial color={0x23190f} flatShading />
        {/* wheelAxis */}
        <mesh castShadow receiveShadow>
          <boxGeometry args={[10, 10, 6]} />
          <meshPhongMaterial color={0x59332e} flatShading />
        </mesh>
      </mesh>
      {/* wheelProtecL */}
      <mesh position={[25, -20, -25]} castShadow receiveShadow>
        <boxGeometry args={[30, 15, 10, 1, 1, 1]} />
        <meshPhongMaterial color={0xd8d0d1} flatShading />
      </mesh>
      {/* wheelTireL */}
      <mesh position={[25, -28, -25]} castShadow receiveShadow>
        <boxGeometry args={[24, 24, 4]} />
        <meshPhongMaterial color={0x23190f} flatShading />
        {/* wheelAxis */}
        <mesh castShadow receiveShadow>
          <boxGeometry args={[10, 10, 6]} />
          <meshPhongMaterial color={0x59332e} flatShading />
        </mesh>
      </mesh>
      {/* wheelTireB */}
      <mesh position={[-35, -5, 0]} scale={0.5} castShadow receiveShadow>
        <boxGeometry args={[24, 24, 4]} />
        <meshPhongMaterial color={0x23190f} flatShading />
        <mesh castShadow receiveShadow>
          <boxGeometry args={[10, 10, 6]} />
          <meshPhongMaterial color={0x59332e} flatShading />
        </mesh>
      </mesh>
      <mesh position={[-35, -5, 0]} rotation-z={-0.3} geometry={suspensionGeom}>
        <meshPhongMaterial color={0xf25346} flatShading />
      </mesh>
    </group>
  );
};

export default AirPlane;

function normalize(v: number, vmin: number, vmax: number, tmin: number, tmax: number) {
  const nv = Math.max(Math.min(v, vmax), vmin);
  const dv = vmax - vmin;
  const pc = (nv - vmin) / dv;
  const dt = tmax - tmin;
  const tv = tmin + pc * dt;
  return tv;
}
